package cc.rengu.igas.mcps.core.service.trans.scanpay;

import cc.rengu.igas.mcps.common.constant.McpsParamConstant;
import cc.rengu.igas.mcps.common.enums.RespCodeEnum;
import cc.rengu.igas.mcps.core.service.base.ManageService;
import cc.rengu.igas.mcps.facade.base.BaseResponse;
import cc.rengu.igas.mcps.facade.bean.WorkKeyInfo;
import cc.rengu.igas.mcps.facade.request.TransmitWorkKeyRequest;
import cc.rengu.igas.mcps.facade.response.TransmitWorkKeyResponse;
import cc.rengu.igas.share.common.constant.UserSessionConstant;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.constant.TreeNodeConstant;
import cc.rengu.oltp.service.common.dao.CertInfoMapper;
import cc.rengu.oltp.service.common.dao.SecPlanInfoMapper;
import cc.rengu.oltp.service.common.dao.SrcChannelInfoMapper;
import cc.rengu.oltp.service.common.dao.impl.CertInfoMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.SecPlanInfoMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.SrcChannelInfoMapperImpl;
import cc.rengu.oltp.service.common.entity.CertInfo;
import cc.rengu.oltp.service.common.entity.SecPlanInfo;
import cc.rengu.oltp.service.common.entity.SrcChannelInfo;
import cc.rengu.oltp.service.common.entity.SysParam;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.service.model.BizResponse;
import cc.rengu.oltp.service.realize.SysParamService;
import cc.rengu.oltp.service.realize.impl.SysParamServiceImpl;
import cc.rengu.oltp.utility.util.*;
import com.alibaba.fastjson.JSON;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.security.interfaces.RSAPrivateKey;
import java.util.List;

/**
 * 扫码支付-同步密钥服务
 *
 * @author keyi
 * date 2020/3/18.
 */
public class McpsTransmitWorkKeyService extends ManageService {

    @Override
    protected Object bizBeforeCust() throws Exception {
        TransmitWorkKeyRequest transmitWorkKeyRequest = new TransmitWorkKeyRequest();
        ConvertUtil.convertOutput(transmitWorkKeyRequest);
        return transmitWorkKeyRequest;
    }

    @Override
    protected BizResponse bizProcessCust(Object request) throws Exception {
        BizResponse<BaseResponse> bizResponse = new BizResponse<>();
        bizResponse.setRspSysId(AppParamConstant.SYS_ID);

        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        TransmitWorkKeyRequest transmitWorkKeyRequest = (TransmitWorkKeyRequest) request;
        String instId = transmitWorkKeyRequest.getHeader().getInstId();
        List<WorkKeyInfo> workKeyInfos = transmitWorkKeyRequest.getWorksKeyInfoList();

        //根据交易渠道信息表获取系统安全计划标识
        String srcChannelInfoKey = transmitWorkKeyRequest.getHeader().getInstId().trim() + transmitWorkKeyRequest.getHeader().getSrcSysId().trim() + transmitWorkKeyRequest.getHeader().getChanlId().trim();
        SrcChannelInfo srcChannelInfo = JSON.parseObject(RedisUtil.hashGet(AppParamConstant.CHANNEL_ACQ_INFO_CACHE, srcChannelInfoKey), SrcChannelInfo.class);
        if (null == srcChannelInfo) {
            srcChannelInfoKey = transmitWorkKeyRequest.getHeader().getInstId().trim() + transmitWorkKeyRequest.getHeader().getSrcSysId().trim() + "*";
            srcChannelInfo = JSON.parseObject(RedisUtil.hashGet(AppParamConstant.CHANNEL_ACQ_INFO_CACHE, srcChannelInfoKey), SrcChannelInfo.class);
            if (null == srcChannelInfo) {
                SrcChannelInfoMapper srcChannelInfoMapper = new SrcChannelInfoMapperImpl();
                srcChannelInfo = srcChannelInfoMapper.selectSrcChannelInfoByPrimaryKey(txnInfo.getSrcTxnAuthCfg().getInstId().trim(), txnInfo.getSrcTxnAuthCfg().getMsgSrcId().trim(), txnInfo.getSrcTxnAuthCfg().getTransChannelId().trim());
            }
        }
        if (null == srcChannelInfo) {
            rglog.error("交易渠道<{}>的细分渠道<{}>未配置或已停用！", txnInfo.getSrcTxnAuthCfg().getMsgSrcId(), txnInfo.getSrcTxnAuthCfg().getTransChannelId());
            bizResponse.setRespCode(RespCodeEnum.SEC_PLAN_NOEXIT.getRespCode());
            bizResponse.setRespDesc(RespCodeEnum.SEC_PLAN_NOEXIT.getRespDesc());
            return bizResponse;
        }

        //获取安全计划信息，校验安全计划状态是否正常
        SecPlanInfoMapper secPlanInfoMapper = new SecPlanInfoMapperImpl();
        String secPlanType = McpsParamConstant.SIGN_CERT_TYPE;
        SecPlanInfo secPlanInfo = secPlanInfoMapper.selectSecPlanInfoByPrimaryKey(instId, srcChannelInfo.getSecPlanId(), secPlanType);
        if (secPlanInfo == null) {
            rglog.info("机构:<{}>没有配置安全计划信息，使用默认机构:<800>的安全计划信息", instId);
            instId = AppParamConstant.DEFAULT_INSTID;
            secPlanInfo = secPlanInfoMapper.selectSecPlanInfoByPrimaryKey(instId, srcChannelInfo.getSecPlanId(), secPlanType);
        }
        if (secPlanInfo == null) {
            rglog.info("获取安全计划信息失败");
            bizResponse.setRespCode(RespCodeEnum.SEC_PLAN_NOEXIT.getRespCode());
            bizResponse.setRespDesc(RespCodeEnum.SEC_PLAN_NOEXIT.getRespDesc());
            return bizResponse;
        } else if (!AppParamConstant.YES.equals(secPlanInfo.getSecPlanStatus())) {
            rglog.info("安全计划信息安全计划状态异常:<{}>", secPlanInfo.getSecPlanStatus());
            bizResponse.setRespCode(RespCodeEnum.SEC_PLAN_STATUS_FALSE.getRespCode());
            bizResponse.setRespDesc(RespCodeEnum.SEC_PLAN_STATUS_FALSE.getRespDesc());
            return bizResponse;
        }

        //获取安全计划信息证书
        String certIndex = instId + srcChannelInfo.getSecPlanId() + secPlanType;
        CertInfoMapper certInfoMapper = new CertInfoMapperImpl();
        CertInfo certInfo = certInfoMapper.selectCertInfoByPrimaryKey(certIndex);
        if (certInfo == null) {
            rglog.info("获取证书信息失败,certIndex:<{}>", certIndex);
            bizResponse.setRespCode(RespCodeEnum.CERT_LOAD_INVALID.getRespCode());
            bizResponse.setRespDesc(RespCodeEnum.CERT_LOAD_INVALID.getRespDesc());
            return bizResponse;
        } else if (AppParamConstant.NO.equals(certInfo.getKeyStatus())) {
            rglog.info("证书状态无效,certIndex:<{}>,keyStatus:<{}>", certIndex, certInfo.getKeyStatus());
            bizResponse.setRespCode(RespCodeEnum.CERT_STATUS_INVALID.getRespCode());
            bizResponse.setRespDesc(RespCodeEnum.CERT_STATUS_INVALID.getRespDesc());
            return bizResponse;
        }

        //获取私钥内容
        String privateKeyStr;
        if ("2".equals(certInfo.getCertSaveType())) {
            rglog.info("证书以文件的方式存放，certIndex:<{}>,certSavePath:<{}>", certIndex, certInfo.getCertSavePath());
            try {
                privateKeyStr = FileUtils.readFileToString(new File(certInfo.getCertSavePath()), Charset.defaultCharset());
            } catch (IOException e) {
                rglog.error("读取证书文件异常:{}", e.getMessage());
                bizResponse.setRespCode(RespCodeEnum.SYSTEM_ERROR.getRespCode());
                bizResponse.setRespDesc(RespCodeEnum.SYSTEM_ERROR.getRespDesc());
                return bizResponse;
            }
        } else {
            privateKeyStr = certInfo.getCertValue();
        }

        //对称密钥存放在redis中的时间
        String sessionId = RandomUtil.getUUID();
        SysParamService sysParamService = new SysParamServiceImpl();
        SysParam sysParam = sysParamService.getSysParamInfo(instId, McpsParamConstant.OPMS_SYS_PARAM, McpsParamConstant.SESSION_EXPIRY_TIMES);
        int expirySecond = (sysParam == null || StringUtil.isEmptyOrNull(sysParam.getParamValue())) ? McpsParamConstant.SESSION_DEFAULT_TIMES : Integer.parseInt(sysParam.getParamValue());

        //解密工作密钥获取对称密钥
        RSAPrivateKey privateKey = RSAUtil.getPrivateKeyFromBase64String(privateKeyStr);
        for (WorkKeyInfo workKeyInfo : workKeyInfos) {
            if ("00".equals(workKeyInfo.getKeyType())) {
                String signatureKey = RSAUtil.privateKeyDecrypt(Base64.decodeBase64(workKeyInfo.getKeyValue()), privateKey);
                rglog.debug("会话加签及验签密钥:{}", new String(Base64.decodeBase64(signatureKey)));
                RedisUtil.hashSet(sessionId, UserSessionConstant.SIGNATURE_KEY, new String(Base64.decodeBase64(signatureKey)), expirySecond);
            } else if ("01".equals(workKeyInfo.getKeyType())) {
                String sensitiveKey = RSAUtil.privateKeyDecrypt(Base64.decodeBase64(workKeyInfo.getKeyValue()), privateKey);
                rglog.debug("敏感信息加解密密钥:{}", new String(Base64.decodeBase64(sensitiveKey)));
                RedisUtil.hashSet(sessionId, UserSessionConstant.SENSITIVE_KEY, new String(Base64.decodeBase64(sensitiveKey)), expirySecond);
            } else {
                rglog.info("无法识别的密钥类型，keyType:<{}>", workKeyInfo.getKeyType());
                throw new BizException(RespCodeEnum.BIZ_PARAM_CHECK_ERROR.getRespCode(), RespCodeEnum.BIZ_PARAM_CHECK_ERROR.getRespDesc());
            }
        }
        xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_SESSION, sessionId);
        bizResponse.setRespCode(RespCodeEnum.TRANS_SUCCESS.getRespCode());
        bizResponse.setRespDesc(RespCodeEnum.TRANS_SUCCESS.getRespDesc());
        return bizResponse;
    }

    @Override
    protected void bizAfterCust(Object response) throws Exception {
        BizResponse bizResponse = (BizResponse) response;
        TransmitWorkKeyResponse transmitWorkKeyResponse = (TransmitWorkKeyResponse) bizResponse.getResult();
        ConvertUtil.convertInput(transmitWorkKeyResponse);
    }
}
